package com.xmutca.core.plus.filter;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import com.xmutca.core.common.Receipt;
import com.xmutca.core.common.Redirect;
import com.xmutca.core.common.Results;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 日志拦截器
 * @author: 彭伟煌(pengweihuang@xmutca.com)
 * @create: 2016-07-09 23:43
 */
@Activate(group = {Constants.CONSUMER}, order = -111111111)
public class CustomerExceptionWrapFilter implements Filter {

    private final static Logger logger = LoggerFactory.getLogger(CustomerExceptionWrapFilter.class);

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        logger.info("CustomerExecute Before: Interface -> {}, method -> {}", invoker.getInterface().getName(), invocation.getMethodName());
        Result result = null;
        try {
            result = invoker.invoke(invocation);
        } catch (RpcException exception) {
            logger.error("CustomerException: Interface -> {}, method -> {}, status -> {}, message -> {}", invoker.getInterface().getName(), invocation.getMethodName(), exception.getMessage());
            if (exception.getCause() instanceof ConstraintViolationException) {
                //处理ConstraintViolationException异常
                ConstraintViolationException violationException = (ConstraintViolationException) exception.getCause();
                result = processConstraintViolationException(invoker, invocation, violationException);
                if (result != null) {
                    return result;
                }
            }
            throw exception;
        }
        logger.info("CustomerExecute After: Interface -> {}, method -> {}", invoker.getInterface().getName(), invocation.getMethodName());
        return result;
    }

    /**
     * 处理ConstraintViolationException异常
     * @param invoker
     * @param invocation
     * @param violationException
     * @return
     */
    public Result processConstraintViolationException(Invoker<?> invoker, Invocation invocation, ConstraintViolationException violationException) {
        try {
            Class<?> interfaceClass = invoker.getInterface();
            String methodName = invocation.getMethodName();
            Class<?>[] parameterTypes = invocation.getParameterTypes();
            Method method = interfaceClass.getMethod(methodName, parameterTypes);
            Class<?> returnType = method.getReturnType();

            return new RpcResult(wrapConstraintViolationException(violationException, returnType));
        } catch (NoSuchMethodException e) {
            //忽略，不处理
        }
        return null;
    }

    /**
     * 包装校验数据异常
     * @param exception
     * @return
     */
    public Object wrapConstraintViolationException(ConstraintViolationException exception, Class<?> returnType) {
        Set<ConstraintViolation<?>> exceptions = exception.getConstraintViolations();

        //封装错误信息
        StringBuffer buffer = new StringBuffer("数据校验失败: ");
        List<ConstraintViolation<?>> messages = new ArrayList<>();
        messages.addAll(exceptions);

        for (int i = 0; i < messages.size(); i++) {
            buffer.append(messages.get(i).getMessage());
            if (i != (messages.size() - 1)) {
                buffer.append(",");
            }
        }

        if (com.xmutca.core.common.Result.class.getSimpleName().equals(returnType.getSimpleName())) {
            return new com.xmutca.core.common.Result<Object>(com.xmutca.core.common.Result.Status.ERROR, buffer.toString());
        } else if (Results.class.getSimpleName().equals(returnType.getSimpleName())) {
            return new Results<Object>(com.xmutca.core.common.Result.Status.ERROR, buffer.toString());
        } else if (Receipt.class.getSimpleName().equals(returnType.getSimpleName())) {
            return new Receipt(com.xmutca.core.common.Result.Status.ERROR, buffer.toString());
        }

        return new Redirect.Builder().setMessage(buffer.toString()).build();
    }
}
